﻿using Microsoft.ML;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static MLModel_ConsoleApp.MLModel;
using B.WMS.GoodsDto.Input;
using B.WMS.GoodsDto.Output;
using B.WMS.WMSEntities.GoodsEntities;
using B.WMS.InWareHouseManagement;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.DependencyInjection;

namespace B.WMS.Good
{
    public partial class MLModel : ITransientDependency
    {
        private readonly IRepository<Goods, Guid> _goodsRepository;
        private readonly IRepository<InWareHouseModel, Guid> _inWareHouseModelRepository;
        private readonly IRepository<OutWareHouseModel, Guid> _outWareHouseModelRepository;

        public MLModel(
            IRepository<Goods, Guid> goodsRepository,
            IRepository<InWareHouseModel, Guid> inWareHouseModelRepository,
            IRepository<OutWareHouseModel, Guid> outWareHouseModelRepository)
        {
            _goodsRepository = goodsRepository;
            _inWareHouseModelRepository = inWareHouseModelRepository;
            _outWareHouseModelRepository = outWareHouseModelRepository;
        }

        /// <summary>
        /// 获取商品需求预测
        /// </summary>
        /// <param name="input">预测输入参数</param>
        /// <returns>预测结果</returns>
        public async Task<PredictionResultDto> GetGoodsPrediction(PredictionInputDto input)
        {
            var result = new PredictionResultDto
            {
                StartDate = input.StartDate,
                EndDate = input.EndDate,
                ModelVersion = "1.0"
            };

            try
            {
                // 0. 测试ML模型是否可用
                TestMLModel();

                // 1. 获取历史数据
                if (input.IncludeHistoricalData)
                {
                    var historicalData = await GetHistoricalData(input);
                    result.HistoricalData = historicalData;
                }

                // 2. 生成预测数据
                var predictedData = await GeneratePredictions(input);
                result.PredictedData = predictedData;

                // 3. 计算预测准确度评分
                result.AccuracyScore = CalculateAccuracyScore(result.HistoricalData, result.PredictedData);

                return result;
            }
            catch (Exception ex)
            {
                // 记录错误日志
                Console.WriteLine($"预测过程中发生错误: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 获取历史数据
        /// </summary>
        /// <param name="input">预测输入参数</param>
        /// <returns>历史数据列表</returns>
        private async Task<List<HistoricalDataDto>> GetHistoricalData(PredictionInputDto input)
        {
            var historicalData = new List<HistoricalDataDto>();
            var endDate = DateTime.Now;
            var startDate = endDate.AddDays(-input.HistoricalDataDays);

            // 获取入库数据
            var inWarehouseData = await _inWareHouseModelRepository.GetListAsync(x => 
                x.CreationTime >= startDate && 
                x.CreationTime <= endDate &&
                (string.IsNullOrEmpty(input.GoodsCoding) || x.Relationordernum == input.GoodsCoding));

            // 获取出库数据
            var outWarehouseData = await _outWareHouseModelRepository.GetListAsync(x => 
                x.CreationTime >= startDate && 
                x.CreationTime <= endDate &&
                (string.IsNullOrEmpty(input.GoodsCoding) || x.RelationGoodsnum == input.GoodsCoding));

            // 按日期分组统计
            var groupedData = inWarehouseData
                .GroupBy(x => x.CreationTime.Date)
                .Select(g => new
                {
                    Date = g.Key,
                    InQuantity = g.Sum(x => x.Inwarehousetype),
                    OutQuantity = outWarehouseData
                        .Where(o => o.CreationTime.Date == g.Key)
                        .Sum(o => o.Num)
                })
                .OrderBy(x => x.Date)
                .ToList();

            foreach (var item in groupedData)
            {
                historicalData.Add(new HistoricalDataDto
                {
                    CreateTime = item.Date,
                    Num = item.InQuantity - item.OutQuantity, // 净库存变化
                    Year = item.Date.Year,
                    Month = item.Date.Month,
                    Day = item.Date.Day
                });
            }

            return historicalData;
        }

        /// <summary>
        /// 生成预测数据
        /// </summary>
        /// <param name="input">预测输入参数</param>
        /// <returns>预测数据列表</returns>
        private async Task<List<PredictedDataDto>> GeneratePredictions(PredictionInputDto input)
        {
            var predictedData = new List<PredictedDataDto>();
            var currentDate = input.StartDate;

            while (currentDate <= input.EndDate)
            {
                // 创建模型输入
                var modelInput = new ModelInput
                {
                    Createtime = currentDate.ToString("yyyy-MM-dd"),
                    Num = 0, // 这个值会被模型忽略，因为我们预测的是未来
                    Year = currentDate.Year,
                    Month = currentDate.Month,
                    Day = currentDate.Day
                };

                try
                {
                    // 使用ML模型进行预测
                    var prediction = PredictWithCustomPath(modelInput);
                    
                    predictedData.Add(new PredictedDataDto
                    {
                        PredictedDate = currentDate,
                        PredictedNum = prediction.Score,
                        Confidence = CalculateConfidence(prediction.Score),
                        Score = prediction.Score
                    });
                }
                catch (Exception ex)
                {
                    // 如果预测失败，使用默认值
                    Console.WriteLine($"预测日期 {currentDate:yyyy-MM-dd} 时发生错误: {ex.Message}");
                    predictedData.Add(new PredictedDataDto
                    {
                        PredictedDate = currentDate,
                        PredictedNum = 0,
                        Confidence = 0.5,
                        Score = 0
                    });
                }

                currentDate = currentDate.AddDays(input.IntervalDays);
            }

            return predictedData;
        }

        /// <summary>
        /// 计算置信度
        /// </summary>
        /// <param name="score">预测分数</param>
        /// <returns>置信度值</returns>
        private double CalculateConfidence(float score)
        {
            // 基于预测分数的绝对值计算置信度
            // 这里使用简单的线性映射，实际应用中可能需要更复杂的算法
            var absScore = Math.Abs(score);
            var confidence = Math.Min(0.95, Math.Max(0.1, absScore / 100.0));
            return Math.Round(confidence, 2);
        }

        /// <summary>
        /// 计算预测准确度评分
        /// </summary>
        /// <param name="historicalData">历史数据</param>
        /// <param name="predictedData">预测数据</param>
        /// <returns>准确度评分</returns>
        private double CalculateAccuracyScore(List<HistoricalDataDto> historicalData, List<PredictedDataDto> predictedData)
        {
            if (historicalData.Count == 0 || predictedData.Count == 0)
                return 0.0;

            // 简单的准确度计算：基于历史数据的方差和预测数据的稳定性
            var historicalVariance = CalculateVariance(historicalData.Select(x => x.Num));
            var predictedVariance = CalculateVariance(predictedData.Select(x => x.PredictedNum));

            // 如果预测方差较小且历史方差合理，则认为准确度较高
            var accuracy = Math.Max(0.1, Math.Min(0.95, 1.0 - (predictedVariance / (historicalVariance + 1))));
            return Math.Round(accuracy, 2);
        }

        /// <summary>
        /// 计算方差
        /// </summary>
        /// <param name="values">数值列表</param>
        /// <returns>方差值</returns>
        private double CalculateVariance(IEnumerable<float> values)
        {
            var valueList = values.ToList();
            if (valueList.Count == 0) return 0;

            var mean = valueList.Average();
            var variance = valueList.Sum(x => Math.Pow(x - mean, 2)) / valueList.Count;
            return variance;
        }

        /// <summary>
        /// 测试ML模型是否可用
        /// </summary>
        private void TestMLModel()
        {
            try
            {
                var modelPath = GetModelPath();
                Console.WriteLine($"ML模型路径: {modelPath}");
                
                var testInput = new ModelInput
                {
                    Createtime = DateTime.Now.ToString("yyyy-MM-dd"),
                    Num = 100,
                    Year = DateTime.Now.Year,
                    Month = DateTime.Now.Month,
                    Day = DateTime.Now.Day
                };
                
                var result = PredictWithCustomPath(testInput);
                Console.WriteLine($"ML模型测试成功，预测结果: {result.Score}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ML模型测试失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 使用自定义路径进行预测
        /// </summary>
        /// <param name="input">模型输入</param>
        /// <returns>预测结果</returns>
        private ModelOutput PredictWithCustomPath(ModelInput input)
        {
            try
            {
                var modelPath = GetModelPath();
                var mlContext = new MLContext();
                ITransformer mlModel = mlContext.Model.Load(modelPath, out var _);
                var predEngine = mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);
                return predEngine.Predict(input);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ML模型预测失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 获取模型文件路径
        /// </summary>
        /// <returns>模型文件路径</returns>
        private string GetModelPath()
        {
            // 尝试多个可能的路径
            var possiblePaths = new[]
            {
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MLModel.mlnet"),
                Path.Combine(Directory.GetCurrentDirectory(), "MLModel.mlnet"),
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot", "MLModel.mlnet"),
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "MLModel_ConsoleApp", "MLModel.mlnet"),
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "MLModel_ConsoleApp", "MLModel.mlnet")
            };

            foreach (var path in possiblePaths)
            {
                if (File.Exists(path))
                {
                    Console.WriteLine($"找到ML模型文件: {path}");
                    return path;
                }
            }

            // 如果找不到文件，抛出详细错误信息
            var searchedPaths = string.Join("\n", possiblePaths);
            throw new FileNotFoundException($"找不到ML模型文件 MLModel.mlnet。已搜索的路径:\n{searchedPaths}");
        }
    }
}
